package com.zj.base.view_injector;

import android.app.Activity;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.zj.base.view_injector.annotantions.IdRes;
import com.zj.base.view_injector.annotantions.LayoutRes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zhaojie on 2018/6/5.
 */


@SuppressWarnings("unused")
public final class ViewBinder {

    private ViewBinder() {
    }

    //if params was activity
    public static void bind(Activity activity) {
        invokeActivity(activity, activity.getClass(), getInfo(activity));
    }

    public static void bind(View view) {
        invokeView(view, view.getClass(), getInfo(view));
    }

    public static View bind(Fragment fragment) {
        return invokeFragment(fragment, getInfo(fragment));
    }


    /**
     * use to dialog 、popWindow 、customViews
     */
    public static void bind(Object object, View view) {
        invokeObject(object, view, getInfo(object));
    }

    private static BinderInfo getInfo(Object obj) {
        Class cls = obj.getClass();
        return initIds(cls);
    }


    private static View invokeFragment(Fragment fragment, BinderInfo info) {
        View v = null;
        try {
            //findView
            Map<String, Integer> idMaps = info.map;
            for (Map.Entry<String, Integer> entry : idMaps.entrySet()) {
                View root = fragment.getView();
                if (root != null) {
                    Field field = fragment.getClass().getDeclaredField(entry.getKey());
                    View view = root.findViewById(entry.getValue());
                    field.setAccessible(true);
                    field.set(fragment, view);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return v;
    }

    private static void invokeView(Object obj, Class cls, BinderInfo info) {
        try {
            //findView
            Map<String, Integer> idMaps = info.map;
            Method method = cls.getMethod("findViewById", int.class);
            for (Map.Entry<String, Integer> entry : idMaps.entrySet()) {
                Field field = cls.getDeclaredField(entry.getKey());
                View view = (View) method.invoke(obj, entry.getValue());
                field.setAccessible(true);
                field.set(obj, view);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void invokeActivity(Object obj, Class cls, BinderInfo info) {
        try {
            //setContentView
            Method method_set = cls.getMethod("setContentView", int.class);
            method_set.invoke(obj, info.layout);

            //findView
            Map<String, Integer> idMaps = info.map;
            Method method = cls.getMethod("findViewById", int.class);
            for (Map.Entry<String, Integer> entry : idMaps.entrySet()) {
                Field field = cls.getDeclaredField(entry.getKey());
                View view = (View) method.invoke(obj, entry.getValue());
                field.setAccessible(true);
                field.set(obj, view);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void invokeObject(Object object, View view, BinderInfo info) {
        try {
            //findView
            Map<String, Integer> idMaps = info.map;
            for (Map.Entry<String, Integer> entry : idMaps.entrySet()) {
                Field field = object.getClass().getDeclaredField(entry.getKey());
                View v = view.findViewById(entry.getValue());
                field.setAccessible(true);
                field.set(object, v);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static BinderInfo initIds(Class cls) {
        Map<String, Integer> idMap = new HashMap<>();
        int layoutId = parseLayoutId(cls);
        parseField(cls, idMap);
        return new BinderInfo(layoutId, idMap);
    }

    private static int parseLayoutId(Class cls) {
        try {
            LayoutRes annotation = (LayoutRes) cls.getAnnotation(LayoutRes.class);
            if (annotation != null) {
                return annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    private static void parseField(Class cls, Map<String, Integer> idMap) {
        try {
            Field[] fields = cls.getDeclaredFields();
            for (Field f : fields) {
                IdRes res = f.getAnnotation(IdRes.class);
                if (res != null)
                    idMap.put(f.getName(), res.value());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class BinderInfo {
        public int layout = -1;
        public Map<String, Integer> map;

        public BinderInfo(int layout, Map<String, Integer> map) {
            this.layout = layout;
            this.map = map;
        }
    }
}